package com.example.travelgd.service;

import com.example.travelgd.entity.News;
import com.example.travelgd.repository.NewsMapper;
import com.example.travelgd.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class NewsService {

    @Autowired
    private NewsMapper newsMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private WebSocketServer webSocketServer;

    private static final String NEWS_CACHE_KEY = "travelgd:news";
    private static final String NEWS_READ_COUNT_KEY = "travelgd:news:readcount:";
    private static final String NEWS_READ_USERS_KEY = "travelgd:news:readers:";
    private static final long CACHE_EXPIRE_TIME = 5;
    private static final TimeUnit CACHE_EXPIRE_UNIT = TimeUnit.MINUTES;
    private static final int READ_COUNT_BATCH = 10; // 累积多少次更新一次数据库

    /**
     * 生成列表缓存key
     */
    private String generateListCacheKey(Integer page) {
        return NEWS_CACHE_KEY + ":list:page:" + page;
    }

    /**
     * 生成详情缓存key
     */
    private String generateDetailCacheKey(Long id) {
        return NEWS_CACHE_KEY + ":detail:" + id;
    }

    /**
     * 获取动态列表
     */
    public List<News> getNewsList(Integer page, Integer pageSize) {
        String cacheKey = generateListCacheKey(page);

        // 先从缓存获取
        List<News> cachedList = (List<News>) redisTemplate.opsForValue().get(cacheKey);
        if (cachedList != null) {
            log.info("从缓存获取动态列表: {}", cachedList);
            return cachedList;
        }

        // 计算分页偏移量
        int offset = (page - 1) * pageSize;

        // 缓存未命中,从数据库查询
        List<News> newsList = newsMapper.selectAllPublishedWithPage(offset, pageSize);
        log.info("从数据库获取动态列表: {}", newsList);

        // 放入缓存
        redisTemplate.opsForValue().set(cacheKey, newsList,
                CACHE_EXPIRE_TIME, CACHE_EXPIRE_UNIT);

        return newsList;
    }

    /**
     * 获取动态详情
     */
    public News getNewsDetail(Long id, String userId) {
        try {
            String cacheKey = generateDetailCacheKey(id);
            News cachedNews = (News) redisTemplate.opsForValue().get(cacheKey);
            if (cachedNews != null) {
                // 更新阅读量
                updateReadCount(id, userId);
                log.info("从缓存获取动态详情: {}", cachedNews);
                return cachedNews;
            }

            News news = newsMapper.selectById(id);
            if (news != null) {
                redisTemplate.opsForValue().set(cacheKey, news,
                        CACHE_EXPIRE_TIME, CACHE_EXPIRE_UNIT);
                // 更新阅读量
                updateReadCount(id, userId);
                log.info("从数据库获取动态详情: {}", news);
            }
            return news;
        } catch (Exception e) {
            log.error("获取动态详情失败, id: {}", id, e);
            throw e;
        }
    }

    /**
     * 更新阅读量
     */
    private void updateReadCount(Long id, String userId) {
        try {
            String readUsersKey = NEWS_READ_USERS_KEY + id;
            String countKey = NEWS_READ_COUNT_KEY + id;

            // 检查用户是否已读
            Boolean hasRead = redisTemplate.opsForSet().isMember(readUsersKey, userId);
            if (!hasRead) {
                // 添加用户到已读集合
                redisTemplate.opsForSet().add(readUsersKey, userId);
                // 设置24小时过期
                redisTemplate.expire(readUsersKey, 24, TimeUnit.HOURS);

                // 增加阅读计数
                Long count = redisTemplate.opsForValue().increment(countKey);

                // 每累积READ_COUNT_BATCH次更新一次数据库
                if (count != null && count % READ_COUNT_BATCH == 0) {
                    incrementReadCountInDb(id, READ_COUNT_BATCH);
                }
            }
        } catch (Exception e) {
            log.error("更新阅读量失败, id: {}, userId: {}", id, userId, e);
        }
    }

    /**
     * 更新数据库中的阅读量
     */
    @Async
    public void incrementReadCountInDb(Long id, int increment) {
        try {
            // 更新数据库
            newsMapper.incrementReadCount(id);
            // 清除详情缓存，强制下次重新获取
            String cacheKey = generateDetailCacheKey(id);
            redisTemplate.delete(cacheKey);
            log.info("批量更新阅读数成功, id: {}, increment: {}", id, increment);
        } catch (Exception e) {
            log.error("更新数据库阅读数失败, id: {}", id, e);
        }
    }

    /**
     * 添加动态
     */
    public boolean addNews(News news) {
        try {
            // 设置默认值
            news.setCreateTime(LocalDateTime.now());
            news.setUpdateTime(LocalDateTime.now());
            news.setStatus(news.getStatus() == null ? 1 : news.getStatus());
            news.setReadCount(0);

            if (newsMapper.insert(news) > 0) {
                refreshCache();
                webSocketServer.sendToAll("NEWS_UPDATE");
                log.info("动态添加成功并推送更新消息，ID: {}", news.getId());
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("添加动态失败", e);
            return false;
        }
    }

    /**
     * 更新动态
     */
    public boolean updateNews(News news) {
        try {
            news.setUpdateTime(LocalDateTime.now());
            if (newsMapper.updateById(news) > 0) {
                clearCache();
                webSocketServer.sendToAll("NEWS_UPDATE");
                log.info("动态更新成功并推送更新消息，ID: {}", news.getId());
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("更新动态失败", e);
            return false;
        }
    }

    /**
     * 删除动态
     */
    public boolean deleteNews(Long id) {
        try {
            if (newsMapper.deleteById(id) > 0) {
                refreshCache();
                webSocketServer.sendToAll("NEWS_UPDATE");
                log.info("动态删除成功并推送更新消息，ID: {}", id);
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("删除动态失败", e);
            return false;
        }
    }

    /**
     * 清除所有动态相关缓存
     */
    private void clearCache() {
        try {
            String pattern = NEWS_CACHE_KEY + "*";
            Set<String> keys = redisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.info("清除动态缓存成功, keys: {}", keys);
            }
        } catch (Exception e) {
            log.error("清除动态缓存失败", e);
        }
    }

    /**
     * 刷新动态缓存
     */
    private void refreshCache() {
        try {
            clearCache();
            // 刷新首页列表缓存
            String firstPageCacheKey = generateListCacheKey(1);
            List<News> firstPageNews = newsMapper.selectAllPublishedWithPage(0, 10);
            redisTemplate.opsForValue().set(firstPageCacheKey, firstPageNews,
                    CACHE_EXPIRE_TIME, CACHE_EXPIRE_UNIT);
            log.info("刷新动态缓存成功");
        } catch (Exception e) {
            log.error("刷新动态缓存失败", e);
        }
    }
}